package net.letcode.ct.utils;

import net.letcode.core.utils.common.StringUtils;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * JDBC工具类		<br />
 * @author i@xiaowo.me
 * @date 2015年6月16日
 *
 */
public class JdbcUtils {
	
	/**
	 * 所支持的数据库类型		<br>
	 * 目前仅支持MySQL、MariaDB
	 * @author i@xiaowo.me
	 * @date 2015年7月3日
	 *
	 */
	public static enum DbType{
		MySQL, MariaDB, Oracle, SQLServer
	}
	
	/** MySQL驱动 */
	private static final String DRIVER_MYSQL = "com.mysql.cj.jdbc.Driver";
	
	/** MariaDB驱动 */
	private static final String DRIVER_MARIADB = "org.mariadb.jdbc.Driver";
	
	/** MySQL连接模板 */
	private static final String URL_TEMPLATE_MySQL = "jdbc:mysql://%s:%d/%s?useUnicode=true&characterEncoding=utf-8";
	
	/** MariaDB连接模板 */
	private static final String URL_TEMPLATE_MariaDB = "jdbc:mariadb://%s:%d/%s?useUnicode=true&characterEncoding=utf-8";
	
	/** 主机地址 */
	private static String HOST;
	
	/** 主机端口 */
	private static Integer PORT;
	
	/** 用户名 */
	private static String USER;
	
	/** 密码 */
	private static String PASSWORD;
	
	/** 数据库 */
	private static String DataBase;
	
	/** 数据库驱动 */
	private static String DRIVER;
	
	/** 连接地址 */
	private static String URL;
	
	/** 是否已初始化配置 */
	private static boolean hasInit = false;
	
	/**
	 * 简化版初始化方法，默认为MySQL数据库
	 * @author i@xiaowo.me
	 * @date 2015年6月16日
	 * @param host		主机地址
	 * @param port		主机端口
	 * @param user		用户名
	 * @param password	密码
	 * @param database	数据库
	 */
	public static void init(String host, Integer port, String user, String password, String database){
		init(host, port, user, password, database, DbType.MySQL);
	}
	
	/**
	 * 完整版初始化方法
	 * @author i@xiaowo.me
	 * @date 2015年6月16日
	 * @param host		主机地址
	 * @param port		主机端口
	 * @param user		用户名
	 * @param password	密码
	 * @param database	数据库
	 * @param dbType	数据库类型
	 */
	public static void init(String host, Integer port, String user, String password, String database, DbType dbType){
		if(StringUtils.isNotBlank(host) && StringUtils.isNotBlank(user) && StringUtils.isNotBlank(password) && StringUtils.isNotBlank(database) && (dbType != null) && (port != null)){
			HOST = host;
			PORT = port;
			USER = user;
			PASSWORD = password;
			DataBase = database;
			try {
				switch (dbType) {
				case MySQL:
					DRIVER = DRIVER_MYSQL;
					URL = String.format(URL_TEMPLATE_MySQL, HOST, PORT, DataBase);
					break;
				case MariaDB:
					DRIVER = DRIVER_MARIADB;
					URL = String.format(URL_TEMPLATE_MariaDB, HOST, PORT, DataBase);
					break;
				default:
					break;
				}
				Class.forName(DRIVER);
				hasInit = true;
				System.out.println("【JdbcUtils】初始化成功");
			} catch (ClassNotFoundException e) {
				System.out.println(String.format("【JdbcUtils】初始化失败：无法加载驱动(%s)", DRIVER));
			}
		}else{
			System.out.println(String.format("【JdbcUtils】初始化失败：参数错误\nhost：%s\nport：%d\nuser：%s\npassword：%s\ndbType：%s\ndatabase：%s", host, port, user, password, dbType, database));
		}
	}
	
	/**
	 * 获取数据库链接
	 * @author i@xiaowo.me
	 * @date 2015年6月16日
	 * @return
	 */
	public static Connection getConn(){
		Connection conn = null;
		if(hasInit){
			try {
				conn = DriverManager.getConnection(URL, USER, PASSWORD);
			} catch (SQLException e) {
				System.out.println(String.format("【JdbcUtils】获取连接失败\nurl：%s\nuser：%s\npassword：%s", URL, USER, PASSWORD));
				e.printStackTrace();
			}
		}else{
			System.out.println("【JdbcUtils】获取连接失败：未初始化，请调用init()方法");
		}
		return conn;
	}
	
	/**
	 * 关闭数据库链接
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param rs
	 * @param stmt
	 * @param conn
	 */
	public static void close(ResultSet rs, Statement stmt, Connection conn){
		//关闭ResultSet
		try {
			if(rs != null){
				rs.close();
				rs = null;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		//关闭Statement
		try {
			if(stmt != null){
				stmt.close();
				stmt = null;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		//关闭Connection
		try {
			if(conn != null){
				conn.close();
				conn = null;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 普通查询
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param sql
	 * @param paramters
	 * @return
	 */
	public static List<Object> query(String sql, String... paramters){
		List<Object> result = new ArrayList<Object>();
		
		Connection conn = getConn();
		if(conn != null){
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				ps = conn.prepareStatement(sql);
				if(ps != null){
					for(int i = 0; i < paramters.length; i++){
						ps.setString(i + 1, paramters[i]);
					}
					rs = ps.executeQuery();
					while(rs.next()){
						result.add(rs.getObject(1));
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				close(rs, ps, conn);
			}
		}
		
		return result;
	}
	
	/**
	 * 执行查询，返回单个String，如果有多个返回值
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param sql
	 * @param paramters
	 */
	public static String queryString(String sql, String... paramters){
		String result = null;
		
		Connection conn = getConn();
		if(conn != null){
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				ps = conn.prepareStatement(sql);
				if(ps != null){
					for(int i = 0; i < paramters.length; i++){
						ps.setString(i + 1, paramters[i]);
					}
					rs = ps.executeQuery();
					int total = 0;
					while(rs.next()){
						result = rs.getString(1);
						total++;
					}
					if(total != 1){
						result = null;
						System.out.println(String.format("【JdbcUtils】查询失败：返回多个结果\nsql：%s\nparamters：", sql, Arrays.toString(paramters)));
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				close(rs, ps, conn);
			}
		}
		
		return result;
	}
	
	/**
	 * 执行查询，返回单个Integer
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param sql
	 * @param paramters
	 */
	public static Integer queryInt(String sql, String... paramters){
		Integer result = null;
		Connection conn = getConn();
		if(conn != null){
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				ps = conn.prepareStatement(sql);
				if(ps != null){
					for(int i = 0; i < paramters.length; i++){
						ps.setString(i + 1, paramters[i]);
					}
					rs = ps.executeQuery();
					int total = 0;
					while(rs.next()){
						result = rs.getInt(1);
						total++;
					}
					if(total != 1){
						result = null;
						System.out.println(String.format("【JdbcUtils】查询失败：返回多个结果\nsql：%s\nparamters：" ,sql, Arrays.toString(paramters)));
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				close(rs, ps, conn);
			}
		}
		return result;
	}
	
	/**
	 * 执行查询，返回单个Long
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param sql
	 * @param paramters
	 */
	public static Long queryLong(String sql, String... paramters){
		Long result = null;
		Connection conn = getConn();
		if(conn != null){
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				ps = conn.prepareStatement(sql);
				if(ps != null){
					for(int i = 0; i < paramters.length; i++){
						ps.setString(i + 1, paramters[i]);
					}
					rs = ps.executeQuery();
					int total = 0;
					while(rs.next()){
						result = rs.getLong(1);
						total++;
					}
					if(total != 1){
						result = null;
						System.out.println(String.format("【JdbcUtils】查询失败：返回多个结果\nsql：%s\nparamters：", sql, Arrays.toString(paramters)));
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				close(rs, ps, conn);
			}
		}
		return result;
	}
	
	/**
	 * 执行查询，返回单个Float
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param sql
	 * @param paramters
	 */
	public static Float queryFloat(String sql, String... paramters){
		Float result = null;
		Connection conn = getConn();
		if(conn != null){
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				ps = conn.prepareStatement(sql);
				if(ps != null){
					for(int i = 0; i < paramters.length; i++){
						ps.setString(i + 1, paramters[i]);
					}
					rs = ps.executeQuery();
					int total = 0;
					while(rs.next()){
						result = rs.getFloat(1);
						total++;
					}
					if(total != 1){
						result = null;
						System.out.println(String.format("【JdbcUtils】查询失败：返回多个结果\nsql：%s\nparamters：", sql, Arrays.toString(paramters)));
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				close(rs, ps, conn);
			}
		}
		return result;
	}
	
	/**
	 * 执行查询，返回单个Double
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param sql
	 * @param paramters
	 */
	public static Double queryDouble(String sql, String... paramters){
		Double result = null;
		Connection conn = getConn();
		if(conn != null){
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				ps = conn.prepareStatement(sql);
				if(ps != null){
					for(int i = 0; i < paramters.length; i++){
						ps.setString(i + 1, paramters[i]);
					}
					rs = ps.executeQuery();
					int total = 0;
					while(rs.next()){
						result = rs.getDouble(1);
						total++;
					}
					if(total != 1){
						result = null;
						System.out.println(String.format("【JdbcUtils】查询失败：返回多个结果\nsql：%s\nparamters：", sql, Arrays.toString(paramters)));
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				close(rs, ps, conn);
			}
		}
		return result;
	}
	
	/**
	 * 执行查询，返回单个BigDecimal
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param sql
	 * @param paramters
	 */
	public static BigDecimal queryBigDecimal(String sql, String... paramters){
		BigDecimal result = null;
		Connection conn = getConn();
		if(conn != null){
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				ps = conn.prepareStatement(sql);
				if(ps != null){
					for(int i = 0; i < paramters.length; i++){
						ps.setString(i + 1, paramters[i]);
					}
					rs = ps.executeQuery();
					int total = 0;
					while(rs.next()){
						result = rs.getBigDecimal(1);
						total++;
					}
					if(total != 1){
						result = null;
						System.out.println(String.format("【JdbcUtils】查询失败：返回多个结果\nsql：%s\nparamters：", sql, Arrays.toString(paramters)));
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				close(rs, ps, conn);
			}
		}
		return result;
	}
	
	/**
	 * 执行查询，返回单个Date
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param sql
	 * @param paramters
	 */
	public static Date queryDate(String sql, String... paramters){
		Date result = null;
		Connection conn = getConn();
		if(conn != null){
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				ps = conn.prepareStatement(sql);
				if(ps != null){
					for(int i = 0; i < paramters.length; i++){
						ps.setString(i + 1, paramters[i]);
					}
					rs = ps.executeQuery();
					int total = 0;
					while(rs.next()){
						result = new Date(rs.getTimestamp(1).getTime());
						total++;
					}
					if(total != 1){
						result = null;
						System.out.println(String.format("【JdbcUtils】查询失败：返回多个结果\nsql：%s\nparamters：", sql, Arrays.toString(paramters)));
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				close(rs, ps, conn);
			}
		}
		return result;
	}
	
	/**
	 * 执行查询，返回单个Map
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param sql
	 * @param paramters
	 * @return
	 */
	public static Map<String, Object> queryMap(String sql, String... paramters){
		Map<String, Object> result = null;
		Connection conn = getConn();
		if(conn != null){
			PreparedStatement ps = null;
			ResultSet rs = null;
			ResultSetMetaData rsmd = null;
			try {
				ps = conn.prepareStatement(sql);
				if(ps != null){
					for(int i = 0; i < paramters.length; i++){
						ps.setString(i + 1, paramters[i]);
					}
					rs = ps.executeQuery();
					rsmd = rs.getMetaData();
					int columns = rsmd.getColumnCount();
					
					int total = 0;
					while(rs.next()){
						result = new LinkedHashMap<String, Object>();
						for(int i = 1; i <= columns; i++){
							result.put(rsmd.getColumnName(i), rs.getObject(i));
						}
						total++;
					}
					if(total != 1){
						result = null;
						System.out.println(String.format("【JdbcUtils】查询失败：返回多个结果\nsql：%s\nparamters：", sql, Arrays.toString(paramters)));
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				close(rs, ps, conn);
			}
		}
		return result;
	}
	
	/**
	 * 执行查询，返回单个多个Map
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param sql
	 * @param paramters
	 * @return
	 */
	public static List<Map<String, Object>> queryMaps(String sql, String... paramters){
		List<Map<String, Object>> result = new ArrayList<Map<String,Object>>();
		Connection conn = getConn();
		if(conn != null){
			PreparedStatement ps = null;
			ResultSet rs = null;
			ResultSetMetaData rsmd = null;
			Map<String, Object> item;
			try {
				ps = conn.prepareStatement(sql);
				if(ps != null){
					for(int i = 0; i < paramters.length; i++){
						ps.setString(i + 1, paramters[i]);
					}
					rs = ps.executeQuery();
					rsmd = rs.getMetaData();
					int columns = rsmd.getColumnCount();
					
					while(rs.next()){
						item = new LinkedHashMap<String, Object>();
						for(int i = 1; i <= columns; i++){
							item.put(rsmd.getColumnName(i), rs.getObject(i));
						}
						result.add(item);
					}
					rsmd = null;
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				close(rs, ps, conn);
			}
		}
		return result;
	}
	
	/**
	 * 插入记录，返回主键值
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param sql
	 * @param paramters
	 * @return	主键值
	 */
	public static Object insert(String sql, String... paramters){
		Object pk = null;
		Connection conn = getConn();
		if(conn != null){
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				conn.setAutoCommit(false);
				ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
				if(ps != null){
					for(int i = 0; i < paramters.length; i++){
						ps.setString(i + 1, paramters[i]);
					}
					ps.executeUpdate();
					conn.commit();
					
					rs = ps.getGeneratedKeys();
					while(rs.next()){
						pk = rs.getObject(1);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
				try {
					if(conn != null){
						conn.rollback();
						pk = null;
					}
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			} finally {
				close(rs, ps, conn);
			}
		}
		return pk;
	}
	
	/**
	 * 执行语句，默认开启事务
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param sql
	 * @param paramters
	 * @return 受影响记录条数
	 */
	public static int execute(String sql, String... paramters){
		int rows = 0;
		Connection conn = getConn();
		if(conn != null){
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				conn.setAutoCommit(false);
				
				ps = conn.prepareStatement(sql);
				if(ps != null){
					for(int i = 0; i < paramters.length; i++){
						ps.setString(i + 1, paramters[i]);
					}
					rows = ps.executeUpdate();
					conn.commit();
				}
			} catch (Exception e) {
				e.printStackTrace();
				try {
					if(conn != null){
						conn.rollback();
						rows = 0;
					}
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			} finally {
				close(rs, ps, conn);
			}
		}
		return rows;
	}
	
	/**
	 * 批量执行语句，默认开启事务
	 * @author i@xiaowo.me
	 * @date 2015年6月17日
	 * @param sql
	 * @return 受影响记录条数
	 */
	public static int[] executeBatch(String sql, List<String[]> paramterList){
		int[] result = null;
		Connection conn = getConn();
		if(conn != null){
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				conn.setAutoCommit(false);
				ps = conn.prepareStatement(sql);
				if(ps != null){
					for(String[] paramters : paramterList){
						for(int i = 0; i < paramters.length; i++){
							ps.setString(i + 1, paramters[i]);
						}
						ps.addBatch(); 
					}
					result = ps.executeBatch();
					conn.commit();
				}
			} catch (Exception e) {
				e.printStackTrace();
				try {
					if(conn != null){
						conn.rollback();
						result = null;
					}
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			} finally {
				close(rs, ps, conn);
			}
		}
		return result;
	}
}
